Map hostedtool.CodeInterpreter to the Anthropic code_execution tool and parse its result blocks - #637
Conversation
4ff822a to
528d67f
Compare
This comment has been minimized.
This comment has been minimized.
528d67f to
5cde5d2
Compare
This comment has been minimized.
This comment has been minimized.
Wire the hosted *hostedtool.CodeInterpreter marker into the Anthropic provider so it enables the server-side code_execution tool, and parse the resulting server_tool_use / code_execution_tool_result response blocks into structured message.CodeInterpreterToolCallContent and CodeInterpreterToolResultContent. This mirrors the existing OpenAI Responses provider mapping so hosted code interpretation behaves the same across providers.
5cde5d2 to
47d149a
Compare
This comment has been minimized.
This comment has been minimized.
# Conflicts: # provider/anthropicprovider/agent.go # provider/anthropicprovider/agent_test.go
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
This PR adds hosted code-interpreter support to the Anthropic provider by mapping the framework’s *hostedtool.CodeInterpreter marker to Anthropic’s server-side code_execution tool, and by parsing corresponding response blocks into the framework’s structured message.CodeInterpreterToolCallContent / message.CodeInterpreterToolResultContent content types.
Changes:
- Map
*hostedtool.CodeInterpreterto Anthropiccode_execution_20250825tool parameters in request construction. - Parse Anthropic
server_tool_use(code_execution) blocks intoCodeInterpreterToolCallContentwith base64-encoded Python source (text/x-python). - Parse Anthropic
code_execution_tool_resultblocks intoCodeInterpreterToolResultContentincluding stdout/stderr and hosted file outputs, with new black-box tests covering request/response behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| provider/anthropicprovider/agent.go | Adds CodeInterpreter → Anthropic code_execution tool mapping and response block parsing for server-side code execution. |
| provider/anthropicprovider/agent_test.go | Adds tests validating request tool emission and structured parsing of code execution call/result blocks. |
| if res.Stderr != "" { | ||
| result.Outputs = append(result.Outputs, &message.TextContent{ | ||
| Text: res.Stderr, | ||
| ContentHeader: message.ContentHeader{RawRepresentation: res}, | ||
| }) | ||
| } | ||
| if res.ErrorCode != "" { | ||
| result.Outputs = append(result.Outputs, &message.TextContent{ | ||
| Text: string(res.ErrorCode), | ||
| ContentHeader: message.ContentHeader{RawRepresentation: res}, | ||
| }) | ||
| } |
# Conflicts: # provider/anthropicprovider/agent_test.go
Parity review: PR #637 — Anthropic
|
There was a problem hiding this comment.
Generated by Go API Consistency Review Agent · sonnet46 · 48.8 AIC · ⌖ 5.81 AIC · ⊞ 6K
| }) | ||
| } | ||
| if res.Stderr != "" { | ||
| result.Outputs = append(result.Outputs, &message.TextContent{ |
There was a problem hiding this comment.
Parity issue: stderr mapped to TextContent instead of ErrorContent
The upstream Python implementation (agent_framework_anthropic/_chat_client.py, case "code_execution_tool_result") maps stderr to Content.from_error(message=content_block.content.stderr), which produces an ErrorContent node. Here the Go implementation maps stderr to &message.TextContent{}. Callers that switch on content type (e.g., to distinguish diagnostic output from normal output) will behave differently across SDKs.
Suggestion: use &message.ErrorContent{Message: res.Stderr, ...} for stderr to match Python semantics. Similarly, error_code (line 339) maps to TextContent in Go but to Content.from_error() in Python — both should use ErrorContent.
Upstream reference: python/packages/anthropic/agent_framework_anthropic/_chat_client.py lines 1338–1356.
| }, | ||
| } | ||
| } | ||
| contents = append(contents, call) |
There was a problem hiding this comment.
Parity note: code_execution tool-call input encoding differs from Python
The Python implementation (_chat_client.py, line 1252–1265) wraps the raw input block as Content.from_text(text=str(content_block.input)) — a plain TextContent containing the string representation of the input dict.
This Go implementation extracts the "code" key from the JSON payload and stores it base64-encoded as DataContent with media_type: text/x-python. While arguably more structured (and analogous to how OpenAI Responses surfaces it), this is a deliberate cross-SDK divergence: a consumer inspecting CodeInterpreterToolCallContent.Inputs[0] will receive a DataContent in Go but a TextContent in Python.
If this encoding difference is intentional, please document it (e.g., in a CHANGELOG entry or an inline comment noting the divergence from Python). If it should align, switch to &message.TextContent{Text: code} or align the Python side to emit DataContent.
# Conflicts: # provider/anthropicprovider/agent_test.go
# Conflicts: # provider/anthropicprovider/agent_test.go
There was a problem hiding this comment.
Warning
Firewall blocked 1 domain
The following domain was blocked by the firewall during workflow execution:
proxy.golang.org
To allow these domains, add them to the
network.allowedlist in your workflow frontmatter:
network:
allowed:
- defaults
- "proxy.golang.org"See Network Configuration for more information.
Generated by Go API Consistency Review Agent · sonnet46 · 134.8 AIC · ⌖ 4.96 AIC · ⊞ 6.4K
| // code_execution tool, mirroring the OpenAI Responses provider. | ||
| tools = append(tools, anthropic.ToolUnionParam{ | ||
| OfCodeExecutionTool20250825: &anthropic.CodeExecutionTool20250825Param{}, | ||
| }) |
There was a problem hiding this comment.
Parity finding — missing anthropic-beta: code-execution-2025-08-25 header
The upstream Python implementation (python/packages/anthropic/agent_framework_anthropic/_chat_client.py) unconditionally includes code-execution-2025-08-25 in its BETA_FLAGS list and routes every request through client.beta.messages.create(), which sends the anthropic-beta header automatically. Without that header the Anthropic Messages API will not recognise the code_execution_20250825 tool or return server_tool_use/code_execution_tool_result blocks.
The Go provider uses the stable client.Messages.New() path and currently sends no anthropic-beta header when *hostedtool.CodeInterpreter is present. The request will likely be rejected or silently ignored by the API.
Upstream evidence: python/packages/anthropic/agent_framework_anthropic/_chat_client.py — BETA_FLAGS = ["mcp-client-2025-04-04", "code-execution-2025-08-25"] and _prepare_betas() merges that list into every request.
Suggested resolution: when *hostedtool.CodeInterpreter is appended to tools, also inject option.WithHeader("anthropic-beta", "code-execution-2025-08-25") into the Messages.New() / Messages.NewStreaming() call, or propagate it via MessageNewParams / an equivalent per-request option so the required beta flag reaches the API.
| if res.Stderr != "" { | ||
| result.Outputs = append(result.Outputs, &message.TextContent{ | ||
| Text: res.Stderr, | ||
| ContentHeader: message.ContentHeader{RawRepresentation: res}, | ||
| }) | ||
| } | ||
| if res.ErrorCode != "" { | ||
| result.Outputs = append(result.Outputs, &message.TextContent{ | ||
| Text: string(res.ErrorCode), |
There was a problem hiding this comment.
Parity finding — stderr and error_code mapped as TextContent rather than as an error signal
The upstream Python implementation (python/packages/anthropic/agent_framework_anthropic/_chat_client.py, case "code_execution_tool_result") distinguishes between output and error semantics:
stderr→Content.from_error(message=..., ...)(error-typed content)BetaCodeExecutionToolResultError.error_code(the error-class result type) →Content.from_error(...)
The Go implementation maps both res.Stderr and res.ErrorCode to &message.TextContent{}, the same type used for stdout. Callers that inspect content type to detect execution failure will receive a plain text block instead of an error-typed content item, diverging from Python's semantic.
Note: res.ErrorCode in the Go SDK (CodeExecutionToolResultBlock.Content.ErrorCode) is a field on the success result type. Verify whether it carries the same semantics as Python's BetaCodeExecutionToolResultError; if so, it should also map to an error content type.
Upstream evidence: python/packages/anthropic/agent_framework_anthropic/_chat_client.py — Content.from_error(message=content_block.content.error_code, ...) for BetaCodeExecutionToolResultError, and Content.from_error(message=content_block.content.stderr, ...) for stderr.
Suggested resolution: Map res.Stderr to a message type carrying error semantics (or document the intentional divergence), and review whether res.ErrorCode represents an error-class result that should be surfaced as an error rather than plain text.
| call.Inputs = message.Contents{ | ||
| &message.DataContent{ | ||
| Data: base64.StdEncoding.EncodeToString([]byte(code)), | ||
| MediaType: "text/x-python", |
There was a problem hiding this comment.
Parity finding — tool-call Inputs shape diverges from Python: DataContent (base64 + MIME) vs TextContent (raw string)
The upstream Python implementation (python/packages/anthropic/agent_framework_anthropic/_chat_client.py, the server_tool_use branch for code_execution) maps the tool-call input as:
inputs=[
Content.from_text(
text=str(content_block.input),
raw_representation=content_block,
)
]This produces a plain TextContent with the raw string representation of the input dict.
The Go implementation extracts the code field from the input JSON and wraps it in a *message.DataContent with MediaType: "text/x-python" and base64-encoded Data. This is a meaningfully different shape:
- The Python
Inputs[0]is aTextContent; the GoInputs[0]is aDataContent. - The Python value is the full input dict as a string (e.g.
{"code": "..."}); the Go value is only the extracted code, base64-encoded.
Callers that pattern-match on Inputs[0] type or decode the content will see different types and encoding across SDKs.
Upstream evidence: python/packages/anthropic/agent_framework_anthropic/_chat_client.py — Content.from_text(text=str(content_block.input), ...) in the server_tool_use / code_execution branch.
Suggested resolution: Align the Inputs element with the Python shape (a TextContent containing the extracted code as plain text), or document this intentional divergence. The DataContent + text/x-python approach is reasonable as an enhancement, but should be explicitly discussed with the cross-SDK design owners.
|
Too many parity issues and open questions. This needs some human love. Closing, thanks anyway! |
What
Wire the hosted
*hostedtool.CodeInterpretermarker into the Anthropic provider (provider/anthropicprovider/agent.go):*hostedtool.CodeInterpreteronto Anthropic's server-sidecode_executiontool (CodeExecutionTool20250825Param), appended toparams.Tools.buildBlockcases for theserver_tool_use(code_execution) andcode_execution_tool_resultblocks, surfacing them as structuredmessage.CodeInterpreterToolCallContent(code as atext/x-pythonDataContent) andmessage.CodeInterpreterToolResultContent(stdout/stderr/error asTextContent, output files asHostedFileContent).Why
This is the code-execution sibling of the existing web-search hosted-tool support. The OpenAI Responses provider already maps
*hostedtool.CodeInterpreter(responses.go,case *hostedtool.CodeInterpreter) and emits the same structuredCodeInterpreterToolCall/CodeInterpreterToolResultcontent. The Anthropic provider had no hosted-tool branch and nocode_executionresult parsing, so hosted code interpretation silently did nothing on Anthropic. This change brings Anthropic in line with the OpenAI/.NET structured mapping so the same agent code works across providers.Tests
Added to the canonical
agent_test.go(black-box, reusing the existinghttptestharness):TestCodeInterpreterToolMapsToCodeExecutionasserts the request builder emits acode_execution/code_execution_20250825tool intools.TestCodeInterpreterResultBlocksBecomeStructuredContentfeedsserver_tool_use+code_execution_tool_resultblocks and asserts structuredCodeInterpreterToolCallContent(decoded Python source) andCodeInterpreterToolResultContent(stdout + hosted file output) are produced.go build ./...,go vet ./provider/anthropicprovider/..., andgo test ./provider/anthropicprovider/...all pass.Open design questions
CodeExecutionTool20250825Param. The SDK also exposes newer variants (20260120,20260521); should the version be configurable (e.g. viaAdditionalProperties) or track the latest?hostedtool.CodeInterpreter.Inputs(hosted file IDs) are not yet forwarded — Anthropic'scode_executioncontainer model differs from OpenAI's. Follow-up if pre-seeding files is needed.server_tool_useinput arrives viainput_json_deltaand is not yet accumulated into the call's code block. Worth a follow-up if streaming code capture is required.encrypted_stdout(EncryptedCodeExecutionResultBlock) is not surfaced; only plaintext stdout/stderr and output files are mapped today.